<?php
const PROJECT_NAME = 'php-gtk-ide';
class IdeWindow extends GtkWindow {
	private $menus = null;
	private $menusToolbar = null;
	protected $status = null;
	protected $history = null;
    protected $pages  = array();
    protected $clipboard = null;
    protected $accelGroup = null;
    protected $toolbar = null;
    protected $notebook = null;
    protected $text = null;
    protected $tags = null;
    public function __construct($paths = null) {
        parent::__construct();

		$this->settings = new Notepad_Settings();
		$this->history = new Notepad_History($this->settings);
		$this->clipboard = new GtkClipBoard();
		$this->accelGroup = new GtkAccelGroup();
        $this->add($this->buildGui());
		Notepad_FileDrop::attach($this, array($this, 'onFilesDrop'));
		$this->add_accel_group($this->accelGroup);
        $this->connect_simple('destroy', array($this, 'quit'));
        $this->set_title('php-gtk-ide');
        $this->maximize();
        $this->set_default_size(800, 600);
        $this->set_position(Gtk::WIN_POS_CENTER);
        $this->show_all();
    }


	protected function buildGui() {
        $vbox = new GtkVBox();
		$this->declareMenus();
		$this->addTextTags();

        $vbox->pack_start($this->buildMenu(), false, false);
        $vbox->pack_start($this->buildToolbar(), false, false);
        $vbox->pack_start($this->buildNotebook(), true, true);
        $vbox->pack_start($this->buildStatusbar(), false, false);

        return $vbox;
    }
    
    
	protected function buildToolbar() {
    	$this->toolbar = new GtkToolbar();
		//$this->toolbar->set_style(Gtk::TOOLBAR_ICONS);
		$this->toolbar->set_tooltips(true);

		
    	foreach($this->menusToolbar as $name => $item) {
            if(is_string($name))
                $item->connect_simple('clicked', array($this, 'onMenuActivate'), $name);

    		$this->toolbar->insert($item, -1);
    	}
    	return $this->toolbar;
    }

    
    protected function declareMenus() {
    	//$menu = new MenuBars();
    	//$this->menus = $menu->createMenu();

$this->menus = array(
            '_Archivo'     => array(
                'new'           => array(
                        'label'         => '_Nuevo',
                        'item'          => new GtkImageMenuItem(Gtk::STOCK_NEW),
                        'key'           => 'n',
                        'modifier'      => Gdk::CONTROL_MASK
                ),
                'open'          => array(
                        'label'         => '_Abrir',
                        'item'          => new GtkImageMenuItem(Gtk::STOCK_OPEN),
                        'key'           => 'o',
                        'modifier'      => Gdk::CONTROL_MASK
                ),
                'save'          => array(
                        'label'         => '_Guardar',
                        'item'          => new GtkImageMenuItem(Gtk::STOCK_SAVE),
                        'key'           => 's',
                        'modifier'      => Gdk::CONTROL_MASK
                ),
                'save_as'       => array(
                        'label'         => 'Guardar _como',
                        'item'          => new GtkImageMenuItem(Gtk::STOCK_SAVE_AS),
                        'key'           => 's',
                        'modifier'      => Gdk::CONTROL_MASK | Gdk::SHIFT_MASK
                ),
                'close'         => array(
                        'label'         => '_Cerrar',
                        'item'          => new GtkImageMenuItem(Gtk::STOCK_CLOSE),
                        'key'           => 'w',
                        'modifier'      => Gdk::CONTROL_MASK
                ),
                'reload'        => array(
                        'label'         => '_Recargar',
                        'item'          => new GtkImageMenuItem(Gtk::STOCK_REFRESH),
                        'key'           => 'F5',
                        'modifier'      => 0
                ),
                'delete'        => array(
                        'label'         => '_Borrar',
                        'item'          => new GtkImageMenuItem(Gtk::STOCK_DELETE),
                        'key'           => null,
                        'modifier'      => null
                ),
'execute'       => array(
                    'label'     => '_Ejecutar',
                    'item'      => new GtkImageMenuItem(Gtk::STOCK_EXECUTE),
                    'key'       => 'e',
                    'modifier'  => Gdk::CONTROL_MASK
                ),
                new GtkSeparatorMenuItem(),
                'quit'          => array(
                        'label'         => '_Salir',
                        'item'          => new GtkImageMenuItem(Gtk::STOCK_QUIT),
                        'key'           => 'q',
                        'modifier'      => Gdk::CONTROL_MASK
                )
            ),
            '_Editar'     => array(
                'cut'           => array(
                        'label'         => '_Cortar',
                        'item'          => new GtkImageMenuItem(Gtk::STOCK_CUT),
                        'key'           => 'x',
                        'modifier'      => Gdk::CONTROL_MASK
                ),
                'copy'          => array(
                        'label'         => 'C_opiar',
                        'item'          => new GtkImageMenuItem(Gtk::STOCK_COPY),
                        'key'           => 'c',
                        'modifier'      => Gdk::CONTROL_MASK
                ),
                'paste'         => array(
                        'label'         => '_Pegar',
                        'item'          => new GtkImageMenuItem(Gtk::STOCK_PASTE),
                        'key'           => 'v',
                        'modifier'      => Gdk::CONTROL_MASK
                ),
                new GtkSeparatorMenuItem(),
                'search'        => array(
                        'label'         => '_Buscar',
                        'item'          => new GtkImageMenuItem(Gtk::STOCK_FIND),
                        'key'           => 'f',
                        'modifier'      => Gdk::CONTROL_MASK
                ),
                'replace'       => array(
                        'label'         => '_Reemplazar',
                        'item'          => new GtkImageMenuItem(Gtk::STOCK_FIND_AND_REPLACE),
                        'key'           => 'r',
                        'modifier'      => Gdk::CONTROL_MASK
                )
            ),
            '_Ver'     => array(
                'style'         => array(
                        'label'         => 'Estilo',
                        'item'          => new GtkImageMenuItem(Gtk::STOCK_FIND),
                        'key'           => 'f',
                        'modifier'      => Gdk::CONTROL_MASK
                )
            ),
            '_Historial'  => array(
            ),
            'E_xtra'    => array(
            ),
            '_Ayuda' => array(
                'todo' => array(
                        'label' => 'Por hacer (TODO)',
                        'item' => new GtkImageMenuItem(null),
                        'key' => 't',
                        'modifier' => Gdk::CONTROL_MASK
                ),
                'about' => array(
                        'label' => 'Acerca de...',
                        'item' => new GtkImageMenuItem(null),
                        'key' => 'a',
                        'modifier' => Gdk::CONTROL_MASK
                )
            )
        );



        //$this->menusToolbar = $menu->createToolBar();


	$this->menusToolbar = array(
                'new'           => GtkToolButton::new_from_stock(Gtk::STOCK_NEW),
                'open'          => GtkToolButton::new_from_stock(Gtk::STOCK_OPEN),
                'save'          => GtkToolButton::new_from_stock(Gtk::STOCK_SAVE),
                'save_as'       => GtkToolButton::new_from_stock(Gtk::STOCK_SAVE_AS),
                'close'         => GtkToolButton::new_from_stock(Gtk::STOCK_CLOSE),
            'reload'    => GtkToolButton::new_from_stock(Gtk::STOCK_REFRESH),
                'delete'        => GtkToolButton::new_from_stock(Gtk::STOCK_DELETE),
            'execute'   => GtkToolButton::new_from_stock(Gtk::STOCK_EXECUTE),
                new GtkSeparatorToolItem(),
            'cut'               => GtkToolButton::new_from_stock(Gtk::STOCK_CUT),
            'copy'              => GtkToolButton::new_from_stock(Gtk::STOCK_COPY),
            'paste'             => GtkToolButton::new_from_stock(Gtk::STOCK_PASTE),
            new GtkSeparatorToolItem(),
            'search'    => GtkToolButton::new_from_stock(Gtk::STOCK_FIND),
            'replace'   => GtkToolButton::new_from_stock(Gtk::STOCK_FIND_AND_REPLACE)
        );


    }
    
    
    protected function buildStatusbar() {
        $this->status = new GtkStatusBar();
        return $this->status;
    }
    

	protected function addTextTags() {
        $this->tags = new GtkTextTagTable();
        $tags = array(
        	'highlight' => array(
        		'foreground' => "black",
        		'background' => "yellow",
        		'size-points' => "18"
        	),
        	'phpTags' => array(
        		'foreground' => '#ff0000'
        	),
        	'reservatedWords' => array(
        		'foreground' => '#6699cc'
        	),
        	'coments' => array(
        		'background' => '#6699cc'
        	)
        );

   		foreach($tags as $strTag => $properties) {
            $tag = new GtkTextTag($strTag);
            foreach($properties as $key => $val) {
            	$tag->set_property($key, $val);
            }
            $this->tags->add($tag);
		}
    }
    
    
    
    protected function buildNotebook() {
        $this->notebook = new GtkNotebook();

        $this->notebook->set_scrollable(true);
        $this->notebook->set_show_tabs(true);
        $this->notebook->set_show_border(true);
        $this->notebook->popup_enable();
        $this->notebook->connect('switch-page', array($this, 'onPageSwitch'));
		Notepad_FileDrop::attach($this->notebook, array($this, 'onFilesDrop'));

        return $this->notebook;
    }
    
	
	
	protected function buildMenu() {
        $menuBar = new GtkMenuBar();

        foreach($this->menus as $parent => $items) {
            $itemParent = new GtkMenuItem($parent);
            $menu = new GtkMenu();

            if($parent === '_Historial') {
                $this->history->setMenu($menu);
                foreach($this->history->get() as $path) {
                    if(is_file($path))
                    	$this->add2history($path);
				}
            } else {
                foreach($items as $name => $item) {
                    if(is_array($item)) {
                        foreach($item as $k => $v)
                            $$k = $v;
                        $item->connect_simple('activate', array($this, 'onMenuActivate'), $name);
                        $item->get_child()->set_label($label);
                        if($key)
                            $item->add_accelerator('activate', $this->accelGroup, constant('GDK::KEY_'.$key), $modifier, GTK::ACCEL_VISIBLE);
                    }
                    $menu->append($item);
                }
            }
            $itemParent->set_submenu($menu);
            $menuBar->add($itemParent);
        }

        return $menuBar;
    }
    
	
	protected function getText($page) {
		$buffer = $this->pages[$page]['buffer'];

        $start = $buffer->get_start_iter();
        $end = $buffer->get_end_iter();

        return $buffer->get_text($start, $end);
    }
    
	
	
	public function openFile($path) {
        foreach($this->pages as $intPage => $dataPage) {
        	if(realpath($dataPage['path']) === realpath($path)) {
        		$page = $intPage;
        		break;
        	}
        }
        if(empty($page)) {
            $file = basename($path);
            $content = file_get_contents($path);
            $page = $this->append2notebook($file, $path, $file, $content);
        }
        $this->history->add($path);
        $this->add2history($path);
        $this->notebook->set_current_page($page);
        return true;
    }


    protected function append2notebook($strLabel, $path = null, $file = null, $content = null) {
        $buffer = new GtkTextBuffer($this->tags);
        //$view = new GtkTextView($buffer);
        $scrwin = new GtkScrolledWindow();
        //$view->set_left_margin(10);
        //$view->set_right_margin(10);
        //$view->set_wrap_mode(Gtk::WRAP_WORD_CHAR);

		$view = new GtkSourceView();

        $scrwin->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
        $scrwin->add($view);
		Notepad_FileDrop::attach($scrwin, array($this, 'onFilesDrop'));
        Notepad_FileDrop::attach($view, array($this, 'onFilesDrop'));

    	$page = $this->notebook->append_page($scrwin, new GtkLabel($strLabel));
        $this->notebook->show_all();

        $this->pages[$page] = array(
            'path'      => $path,
            'file'      => $file,
            'buffer'    => $buffer
        );
        $buffer->connect_simple('modified_changed', array($this, 'onTextModified'), $page);
        $buffer->set_text($content);
        $buffer->set_modified(false);
        return $page;
    }

    
    protected function add2history($path) {
        $menu = $this->history->getMenu();
        $children = $menu->get_children();
        foreach($children as $key => $child) {
        	if(realpath($child->get_child()->get_label()) === realpath($path)) {
        		$exists = true;
        		break;
        	}
        }
        if(empty($exists)) {
            $item = new GtkMenuItem($path);
            $item->connect_simple('activate', array($this, 'openFile'), $path);
            $menu->prepend($item);
        } else {
        	$menu->reorder_child($child, 0);
        }
        $menu->show_all();
    }
    
    public function onMenuActivate($action) {
        clearstatcache();
        $page = $this->notebook->get_current_page();
        $validPage = isset($this->pages[$page]) ? true : false;
        switch($action) {
            case 'new':
                static $n = 0;
                $strLabel = sprintf($this->settings->settings['strings.untitled'], ++$n);
                $page = $this->append2notebook($strLabel);

                $this->notebook->set_current_page($page);
                break;
            case 'open':
                static $last = null;
                $dialog = new GtkFileChooserDialog(
                    PROJECT_NAME,
                    $this,
                    Gtk::FILE_CHOOSER_ACTION_OPEN,
                    array(
                        GTK::STOCK_CANCEL, GTK::RESPONSE_CANCEL,
                        GTK::STOCK_OPEN, GTK::RESPONSE_ACCEPT
                    )
                );
                if($validPage && is_dir(dirname($this->pages[$page]['path'])))
                	$dir = dirname($this->pages[$page]['path']);
                elseif($last && is_dir(dirname($last)))
                	$dir = dirname($last);
                else
                	$dir = dirname(__FILE__);
                $dialog->set_current_folder($dir);
                $res = $dialog->run();
                if($res === GTK::RESPONSE_ACCEPT && $path = realpath($dialog->get_filename())) {
                    if($this->openFile($path))
                    	$last = $path;
                }
                $dialog->destroy();
                break;
            case 'save':
                if(!$validPage)
                    break;

				if(!($this->pages[$page]['path'] && $this->pages[$page]['file'])) {
					$this->onMenuActivate('save_as');
					break;
				}

                file_put_contents($this->pages[$page]['path'], $this->getText($page));
        		$this->pages[$page]['buffer']->set_modified(false);
                break;
            case 'save_as':
                if(!$validPage)
                    break;

                $dialog = new GtkFileChooserDialog(
                    PROJECT_NAME,
                    $this,
                    Gtk::FILE_CHOOSER_ACTION_SAVE,
                    array(
                        GTK::STOCK_CANCEL, GTK::RESPONSE_CANCEL,
                        GTK::STOCK_SAVE, GTK::RESPONSE_ACCEPT
                    )
                );
                if($validPage && is_dir(dirname($this->pages[$page]['path'])))
                	$dir = dirname($this->pages[$page]['path']);
                elseif($last && is_dir(dirname($last)))
                	$dir = dirname($last);
                else
                	$dir = dirname(__FILE__);
                $dialog->set_current_folder($dir);
                $dialog->set_current_name($this->pages[$page]['file']);

                $res = $dialog->run();
                if($res === GTK::RESPONSE_ACCEPT && $path = $dialog->get_filename()) {
                    $file = basename($path);
                    $content = $this->getText($page);

                    file_put_contents($path, $content);
        			$this->pages[$page]['buffer']->set_modified(false);

                    $tab = $this->notebook->get_nth_page($page);
                    $this->notebook->set_tab_label_text($tab, $file);

                    $this->pages[$page]['path'] = $path;
                    $this->pages[$page]['file'] = $file;
                }
                $dialog->destroy();
                break;
            case 'close':
                if(!$validPage)
                    break;

                $this->notebook->remove_page($page);
        		unset($this->pages[$page]);
        		$this->pages = array_values($this->pages);
                break;
            case 'reload':
                if(!$validPage || !$path = realpath($this->pages[$page]['path']))
                    break;

            	$content = file_get_contents($path);
            	$this->pages[$page]['buffer']->set_text($content);
                $this->pages[$page]['buffer']->set_modified(false);
            	break;
            case 'delete':
                if(!($validPage && ($path = realpath($this->pages[$page]['path'])) && is_file($path)))
                    break;

                $dialog = new GtkMessageDialog(
                    $this,
                    Gtk::DIALOG_MODAL | Gtk::DIALOG_DESTROY_WITH_PARENT,
                    Gtk::MESSAGE_QUESTION,
                    Gtk::BUTTONS_YES_NO,
                    $this->settings->get('strings.MSGdelete')
                );
                $result = $dialog->run();
                $dialog->destroy();

                if($result !== Gtk::RESPONSE_YES)
                    break;

                unlink($path);
            	break;
            case 'execute':
                if(!($validPage && ($path = realpath($this->pages[$page]['path'])) && is_file($path)))
                    break;
                exec('"' . ($path) . '"');
                break;
            case 'quit':
                $this->quit();
                break;
            case 'cut':
                if(!$validPage)
                    break;

                $this->pages[$page]['buffer']->cut_clipboard($this->clipboard, true);
                break;
            case 'copy':
                if(!$validPage)
                    break;

                $this->pages[$page]['buffer']->copy_clipboard($this->clipboard);
                break;
            case 'paste':
                if(!$validPage)
                    break;

                $this->pages[$page]['buffer']->paste_clipboard($this->clipboard, null, true);
                break;
            case 'search':
                if(!$validPage)
                    break;

                static $last = null;
                static $offset = 0;

				$hbox = new GtkHBox();
                $label = new GtkLabel('_Buscar por:', true);
                $entry = new GtkEntry();

				$entry->grab_focus();
				$entry->set_text($last);
                $entry->set_activates_default(true);
				$label->set_mnemonic_widget($entry);
				$hbox->pack_start($label);
				$hbox->pack_start($entry);
				$entry->set_activates_default(true);

                $dialog = $this->buildDialog($hbox);
				$res = $dialog->run();
				$dialog->destroy();

				if($res !== Gtk::RESPONSE_OK)
					break;

				if($last !== ($searched = $entry->get_text()))
					$offset = 0;

				$len = strlen($searched);
                $content = $this->getText($page);
                $startPos = stripos($content, $searched, $offset);
                $endPos = $startPos + $len;
                $buffer = $this->pages[$page]['buffer'];
                $start = $buffer->get_iter_at_offset($startPos);
                $end = $buffer->get_iter_at_offset($endPos);
                $line = $buffer->get_iter_at_line($start->get_line());

                $buffer->remove_tag_by_name('highlight', $buffer->get_start_iter(), $buffer->get_end_iter());
                if($startPos === false) {
                	$offset = 0;
                	break;
                }
                $this->text->scroll_to_iter($start, 0);
                $buffer->apply_tag_by_name('highlight', $start, $end);

				$offset	= $endPos;
				$last	= $searched;
                break;
            case 'replace':
                if(!$validPage)
                    break;

                static $search = null;
                static $replace = null;

				$table = new GtkTable(2, 2);

                $labelSearch = new GtkLabel('_Buscar por:', true);
                $entrySearch = new GtkEntry();
				$entrySearch->grab_focus();
				$entrySearch->set_text($search);
                $entrySearch->set_activates_default(true);
				$labelSearch->set_mnemonic_widget($entrySearch);
				$table->attach($labelSearch, 0, 1, 0, 1);
				$table->attach($entrySearch, 1, 2, 0, 1);

                $labelReplace = new GtkLabel('_Reemplazar por:', true);
                $entryReplace = new GtkEntry();
				$entryReplace->set_text($replace);
                $entryReplace->set_activates_default(true);
				$labelReplace->set_mnemonic_widget($entryReplace);
				$table->attach($labelReplace, 0, 1, 1, 2);
				$table->attach($entryReplace, 1, 2, 1, 2);

                $dialog = $this->buildDialog($table);
				$res = $dialog->run();
				$dialog->destroy();

				if($res !== Gtk::RESPONSE_OK)
					break;

				$search	= $entrySearch->get_text();
				$replace = $entryReplace->get_text();
                $backup = $this->getText($page);

                $content = str_ireplace($search, $replace, $backup, $count);
				$this->pages[$page]['buffer']->set_text($content);

                $dialog = new GtkMessageDialog(
                    $this,
                    Gtk::DIALOG_MODAL | Gtk::DIALOG_DESTROY_WITH_PARENT,
                    Gtk::MESSAGE_INFO,
                    Gtk::BUTTONS_YES_NO,
                    null
                );
                $dialog->set_markup(sprintf($this->settings->get('strings.MSGreplace'), $count, $search, $replace));
                $result = $dialog->run();
                $dialog->destroy();

                if($result !== Gtk::RESPONSE_YES)
                    $this->pages[$page]['buffer']->set_text($backup);
                break;
            case 'style':
            	 if(!$validPage)
                    break;

                include_once('Engine/Modules/PHP_Parser.php');
				$parser = new PHP_Parser();
				$parser->parser($this->getText($page), $this->pages[$page]['buffer'], $this->text);
			break;
            case 'about':
            	include_once('core/menu/help/about.php');
            	new About();
            break;
            case 'todo':
            	include_once('core/menu/help/Todo.php');
            	new Todo();
            default:
                return;
        }

        return true;
    }

    
    protected function buildDialog() {
        $dialog = new GtkDialog(
            PROJECT_NAME,
            $this,
            Gtk::DIALOG_DESTROY_WITH_PARENT | Gtk::DIALOG_NO_SEPARATOR,
            array(
            	Gtk::STOCK_CANCEL, Gtk::RESPONSE_CANCEL,
            	Gtk::STOCK_OK, Gtk::RESPONSE_OK
            )
        );
        foreach(func_get_args() as $widget)
        $dialog->vbox->add($widget);
        $dialog->set_default_response(Gtk::RESPONSE_OK);
        $dialog->show_all();
        return $dialog;
    }

    
	public function updateStatus($page, $changed = false) {
		$contextID = $this->status->get_context_id('Message');
		$lastID = $this->status->push($contextID, $this->pages[$page]['path'] . ($changed ? '*' : ''));
        return true;
	}

	
    public function OnPageSwitch(GtkNotebook $ntbk, $page, $nPage) {
        if(!isset($this->pages[$nPage]))
            return false;
        $this->text = $ntbk->get_nth_page($nPage)->get_child();
		$this->onTextModified($nPage);
		return true;
    }

    
    public function onTextModified($page) {
		if(!isset($this->pages[$page]))
			return false;
		$modified = $this->pages[$page]['buffer']->get_modified() ? true : false;
    	$this->menus['_Archivo']['save']['item']->set_sensitive($modified);
    	$this->menusToolbar['save']->set_sensitive($modified);
    	$this->updateStatus($page, $modified);
    	return true;
    }

    
    public function onFilesDrop(GtkWidget $widget, array $arFiles) {
    	foreach($arFiles as $file)
    		$this->openFile($file);
    	return true;
    }

    
    public function quit() {
    	$this->clipboard;
        Gtk::main_quit();
    }
}
?>
